package com.stark.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.stark.core.domain.User;
import com.stark.core.query.UserCondition;
import com.stark.core.util.DateUtil;
import com.stark.dao.UserDao;
import com.stark.service.UserService;

/**
 * 用户接口实现。
 * @author Ben
 */
@Service
public class UserServiceImpl implements UserService {
	
	@Autowired
	private EntityManager entityManager;
	@Autowired
	private UserDao userDao;
	@Autowired
	private PasswordEncoder passwordEncoder;

	@Transactional
	@Override
	public User save(User user) {
		checkUsername(user.getUsername(), user.getId());
		if (user.getId() == null || user.getId() <= 0) {
			if (StringUtils.isNotBlank(user.getPassword())) {
				user.setPassword(passwordEncoder.encode(user.getPassword()));
			}
			entityManager.persist(user);
		} else {
			if (StringUtils.isNotBlank(user.getPassword())) {
				User userDB = get(user.getId());
				if (!passwordEncoder.matches(user.getPassword(), userDB.getPassword())) {
					user.setPassword(passwordEncoder.encode(user.getPassword()));
				}
			}
			user = entityManager.merge(user);
		}
		return user;
	}

	@Transactional
	@Override
	public void delete(Long id) {
		if (id == null || id <= 0) {
			return;
		}
		userDao.deleteById(null);
	}

	@Override
	public User get(Long id) {
		if (id == null || id <= 0) {
			return null;
		}
		User user = userDao.findById(id).orElse(null);
		return user;
	}

	@Override
	public long count(UserCondition condition) {
		long count = userDao.count(createSpecification(condition));
		return count;
	}

	@Override
	public List<User> getList(UserCondition condition, Sort sort) {
		if (sort.isUnsorted()) {
			sort.and(User.DEFAULT_SORT);
		}
		List<User> list = userDao.findAll(createSpecification(condition), sort);
		return list;
	}

	@Override
	public Page<User> getPage(UserCondition condition, Pageable pageable) {
		if (pageable.getSort().isUnsorted()) {
			pageable.getSort().and(User.DEFAULT_SORT);
		}
		Page<User> page = userDao.findAll(createSpecification(condition), pageable);
		return page;
	}
	
	private void checkUsername(String username, Long id) {
		if (StringUtils.isBlank(username)) {
			return;
		}
		User user = userDao.findByUsername(username);
		if (user == null) {
			return;
		}
		if (id == null || id <= 0
				|| !id.equals(user.getId())) {
			throw new RuntimeException("用户名 " + user.getUsername() + " 已存在，请更换！");
		}
	}
	
	private Specification<User> createSpecification(UserCondition condition) {
		Specification<User> spec = new Specification<User>() {

			private static final long serialVersionUID = 6477284229236825304L;

			@Override
			public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
				List<Predicate> predicates = new ArrayList<>();
                if (StringUtils.isNotBlank(condition.getName())) {
					predicates.add(cb.like(root.<String>get("name"), "%" + condition.getName() + "%"));
				}
                if (condition.getSex() != null) {
					predicates.add(cb.equal(root.<Integer>get("sex"), condition.getSex()));
				}
                if (condition.getBirthdayMin() != null) {
                	predicates.add(cb.greaterThanOrEqualTo(root.<Date>get("birthday"), DateUtil.dateMinOfDay(condition.getBirthdayMin())));
                }
                if (condition.getBirthdayMax() != null) {
                	predicates.add(cb.lessThanOrEqualTo(root.<Date>get("birthday"), DateUtil.dateMaxOfDay(condition.getBirthdayMax())));
                }
                return query.where(predicates.toArray(new Predicate[predicates.size()])).getRestriction();
			}
		};
		return spec;
	}

}
